const mongo = require("../../lib/mongo");
const DATE = require("../../lib/date");
const request = require("superagent");
class USER{
    constructor(openId){
        this.openId = openId;
        this.db = {};
        this.userInfo = null;
        this.db.user = new mongo(mydb, 'app.user');
        this.db.log = new mongo(mydb, 'app.user.login.log');
        this.db.payment = new mongo(mydb, 'app.user.payment');
        this.db.package = new mongo(mydb, 'app.user.package');
        this.db.packageUse =  new mongo(mydb, 'app.user.package.use');
        this.db.buyLog = new mongo(mydb, 'app.user.buy.log');
        this.db.useLog = new mongo(mydb, 'app.proxy.ip.record');
        this.db.useCache = new mongo(mydb, 'app.user.cache');
        this.db.useRule = new mongo(mydb, 'app.user.rule');
    }
    /**
     * 登录，若存在用户，则.value有记录，若无，则为null
     * @param wxUserInfo
     * @returns {Promise<void>}
     */
    async login(wxUserInfo){
        try{
            let findData = await this.db.user.findOneAndUpdate({openId:this.openId}, {$inc:{loginTime:1}, $set:{
                    nickname:wxUserInfo.nickname
                    ,ico:wxUserInfo.headimgurl
                }}, {upsert: false});
            this.userInfo = findData.value;
            return findData;
        }catch (e) {
            throw e;
        }
    }
    /**
     * 生成用户的appKey，若数据库存在重复的appKey，则重新生成
     * @returns {Promise<*>}
     */
    async generateAppKey() {
        try{
            const appKey = fun.randomString(16);
            let isHadAppKey = await this.db.user.findOne({appKey:appKey});
            if(!isHadAppKey) return appKey;
            return await this.generateAppKey();
        }catch (e) {
            throw e;
        }
    }
    /**
     * 更新用户信息，新用户则插入新的数据
     * @param obj
     * @returns {Promise<void>}
     */
    async updateUserInfo(obj){
        try{
            let result = await this.db.user.update({openId:this.openId}, {$set:obj});
            if(result.result.upserted){
                this.userInfo = await this.db.user.findOne({_id:result.result.upserted[0]._id});
            } else {
                this.userInfo = await this.db.user.findOne({openId:this.openId});
            }
            if(this.userInfo.tel) this.userInfo.tel =  this.userInfo.tel.substr(0,3) + "****" + this.userInfo.tel.substr(7,4);
            return this.userInfo;
        }catch (e) {
            throw e;
        }
    }
    /**
     * 新增登录记录
     * @returns {Promise<null>}
     */
    async newLoginLog(){
        try{
            await this.db.log.insert({openId: this.openId, created_at:new Date(), time:this.userInfo.loginTime, uid:this.userInfo._id});
            return null;
        }catch (e) {
            throw e;
        }
    }
    /**
     * 创建用户订单
     * couponCode待完善
     * @returns {Promise<void>}
     */
    async createPayment(obj){
        try{
            obj.created_at = new Date();
            obj.pay_at = null;
            let paymentInfo = await this.db.payment.insert(obj), callbackObj;
            let payment_event = `payment-${paymentInfo.ops[0]._id}`;
            switch(obj.type){
                case 1:
                    callbackObj = {url:`http://prxox.dangdo.com/app/api/user/payment/finish/${paymentInfo.ops[0]._id}`, payment_event};
                    break;
                default:
            }
            return callbackObj;
        }catch (e) {
            throw e;
        }
    }
    /**
     * 完成订单，触发完成订单事件
     * @returns {Promise<void>}
     */
    static async finishPayment(paymentId, obj){
        try{
            let paymentDb = new mongo(mydb, 'app.user.payment');
            let userDb = new mongo(mydb, 'app.user');
            let paymentInfo = await paymentDb.findOne({_id:fun.ObjectId(paymentId)});
            if(!paymentInfo) throw new Error("不存在该订单");
            let userInfo = await userDb.findOneAndUpdate({_id:paymentInfo.uid}, {$inc:{"money.balance":paymentInfo.amount, "money.total":paymentInfo.amount}}, {returnOriginal:false});
            switch(obj.type){
                case 1:
                    await paymentDb.update({_id:fun.ObjectId(paymentId)}, {$set:{result:"ok", finished_at:new Date()}});
                    break;
                default:
            }
            return userInfo.value.money
        }catch (e) {
            throw e;
        }
    }
    /**
     * 套餐购买
     * 可购买多个套餐，若存在同一类套餐则有效期顺延，否则就插入新的套餐
     * 代理验证时扣除逻辑是 先买先扣，扣完则按次数扣余额。
     * @returns {Promise<void>}
     */
    async buy(packInfo){
        try{
            let userInfo = await this.db.user.findOneAndUpdate({openId:this.openId}, {$inc:{"money.balance": - packInfo.level.cost, "money.cost":packInfo.level.cost}}, {returnOriginal:false}), buyLogInfo;
            packInfo.uid = userInfo.value._id;
            packInfo.afterPay = userInfo.value.money;
            packInfo.created_at = new Date();
            packInfo.dayInfo = {};
            let hasPack = await this.db.package.findOne({uid: userInfo.value._id, type:packInfo.type, level_id:packInfo.level.level_id});
            console.log(hasPack);
            if(!hasPack){
                let end_at = new Date(packInfo.created_at.getTime() + packInfo.day * 24 * 60 * 60 * 1000);
                packInfo.dayInfo.start = packInfo.created_at;
                packInfo.dayInfo.end = end_at;
                buyLogInfo = await this.db.buyLog.insert(packInfo);
                let newPack = {
                    uid: userInfo.value._id,
                    index:packInfo.index, type:packInfo.type, level_id:packInfo.level.level_id
                    , start_at:packInfo.created_at, end_at
                    , buyLog:[
                        {_id:buyLogInfo.ops[0]._id, start_at:packInfo.created_at, end_at, created_at:packInfo.created_at}
                        ]
                };
                await this.db.package.insert(newPack);
            } else {
                let setData = {}, pushData = {}, now = new Date(), buy_start_at;
                /**
                 * 判断套餐起始时间
                 * 若该套餐的结束时间小于当天，则重设起始时间
                 * 否则，在该结束时间上延长套餐时间
                 */
                if(hasPack.end_at.getTime()<now.getTime()){
                    buy_start_at = packInfo.created_at;
                    setData.start_at = packInfo.created_at;
                    setData.end_at = new Date(packInfo.created_at.getTime() + packInfo.day * 24 * 60 * 60 * 1000);
                } else {
                    buy_start_at = hasPack.end_at;
                    setData.end_at = new Date(hasPack.end_at.getTime() + packInfo.day * 24 * 60 * 60 * 1000);
                }
                packInfo.dayInfo.start = buy_start_at;
                packInfo.dayInfo.end = setData.end_at;
                buyLogInfo = await this.db.buyLog.insert(packInfo);

                pushData.buyLog = { _id: buyLogInfo.ops[0]._id, start_at: buy_start_at, end_at: setData.end_at, created_at: packInfo.created_at};
                await this.db.package.update({_id:hasPack._id}, {$set:setData, $push:pushData});
            }
            return await this.db.package.find({uid: userInfo.value._id});
        }catch (e) {
            throw e;
        }
    }
    /**
     * 获取用户套餐
     * @returns {Promise<void>}
     */
    async getPackList(){
        try{
            let now = new Date();
            now.setHours(0, 0, 0, 0);
            let day = now;
            this.userInfo = await this.db.user.findOne({openId:this.openId});
            let userPackList = await this.db.package.find({ uid: this.userInfo._id, end_at: {$gt:new Date()}});
            let userUsePackList = await this.db.packageUse.find({uid: this.userInfo._id, day});
            for(let pack of userPackList){
                let len = userUsePackList.length;
                for(let i = 0;i<len;i++){
                    if(pack._id.toString() === userUsePackList[i].pack_id.toString()){
                        pack.useInfo = userUsePackList[i];
                        userUsePackList.splice(i,1);
                        break;
                    }
                }
            }
            return userPackList;
        }catch (e) {
            throw e
        }
    }
    /**
     * 获取用户单个套餐
     * @param skip
     * @returns {Promise<*>}
     */
    async getSinglePack(skip){
        try{
            let now = new Date();
            now.setHours(0, 0, 0, 0);
            let day = now;
            this.userInfo = await this.db.user.findOne({openId:this.openId});
            let packInfo = await this.db.package.findOne({ uid: this.userInfo._id, end_at: { $gt: new Date() }}, {skip:skip});
            console.log(skip,packInfo);
            if(packInfo){
                let packUseInfo = await this.db.packageUse.findOne({pack_id: packInfo._id, day});
                if(packUseInfo){
                    packInfo.useInfo = packUseInfo;
                }
            }
            return packInfo;
        }catch (e) {
            throw e
        }
    }
    /**
     * 获取用户使用记录
     * @param page
     * @param day
     * @returns {Promise<{totalItem: *, list: *}>}
     */
    async getUseProxyLog(page = 1, day = null){
        try{
            if(!day) day = new Date().getTime();
            let maxDay = new Date(day).setHours(24,0,0,0), minDay = new Date(day).setHours(0,0,0,0), perPage = 10;
            this.userInfo = await this.db.user.findOne({ openId: this.openId });
            console.log(this.userInfo);
            let query = {uid:this.userInfo._id};
            // query.time = {$lte:maxDay, $gte:minDay};
            console.log(query);
            let totalItem = await this.db.useLog.count(query);
            let totalPage = Math.ceil(totalItem/perPage);
            page = page <= totalPage ? page : totalPage;
            page = page > 0 ? page : 1;
            let list = await this.db.useLog.find(query, { limit: perPage, skip: (page - 1) * perPage, sort: { time: -1 } });
            return {totalItem, list}
        }catch (e) {
            throw e;
        }
    }
    /**
     * 获取用户充值记录
     * @param page
     * @param day
     * @returns {Promise<{totalItem: *, list: *}>}
     */
    async getPaymentList(page = 1, day = null){
        try{
            if(!day) day = new Date();
            let maxDay = new Date(day.setHours(24,0,0,0)), minDay = new Date(day.setHours(0,0,0,0)), perPage = 10;
            this.userInfo = await this.db.user.findOne({openId:this.openId});
            let query = {uid:this.userInfo._id};
            let totalItem = await this.db.payment.count(query);
            let totalPage = Math.ceil(totalItem/perPage);
            page = page <= totalPage ? page : totalPage;
            page = page > 0 ? page : 1;
            let list = await this.db.payment.find(query, {limit:perPage, skip:(page-1)* perPage});
            return {totalItem, list}
        }catch (e) {
            throw e;
        }
    }
    /**
     * 获取用户购买记录
     * @param page
     * @param day
     * @returns {Promise<{totalItem: *, list: *}>}
     */
    async getBuyList(page = 1, day = null){
        try{
            if(!day) day = new Date();
            let maxDay = new Date(day.setHours(24,0,0,0)), minDay = new Date(day.setHours(0,0,0,0)), perPage = 10;
            this.userInfo = await this.db.user.findOne({openId:this.openId});
            let query = {uid:this.userInfo._id};
            let totalItem = await this.db.buyLog.count(query);
            let totalPage = Math.ceil(totalItem/perPage);
            page = page <= totalPage ? page : totalPage;
            page = page > 0 ? page : 1;
            let list = await this.db.buyLog.find(query, {limit:perPage, skip:(page-1)* perPage});
            return {totalItem, list}
        }catch (e) {
            throw e;
        }
    }
    /**
     * 用户添加缓存
     * @param {any} id
     */
    async addCache(id) {
        try {
            this.userInfo = await this.db.user.findOne({ openId: this.openId });
            let reqInfo = await this.db.useLog.findOne({ _id: fun.ObjectId(id) });
            let isCache = await this.db.useCache.findOne({ uid: this.userInfo._id, url: reqInfo.url, method: reqInfo.method });
            if (isCache) return { error: "the cache was already exist" };
            let response = null;
            if (parseInt(reqInfo.statusCode / 100) === 3 || parseInt(reqInfo.statusCode / 100) === 5 || !reqInfo.body) {
                let newReq;
                switch (reqInfo.method) {
                    case "GET":
                        newReq = await request(reqInfo.method, reqInfo.url).buffer(true);
                        break;
                    case "POST":
                        newReq = await request(reqInfo.method, reqInfo.url).set(reqInfo.requestInfo.headers).type(reqInfo.requestInfo.type).send(reqInfo.requestInfo.data).buffer(true);
                        break;
                    default:
                        return { error: "the request method is not surpport" };
                }
                if (newReq) {
                    response = {
                        statusCode: newReq.statusCode,
                        header: newReq.headers,
                        body: newReq.text
                    }
                }
            } else {
                response = {
                    statusCode: reqInfo.statusCode,
                    header: reqInfo.header,
                    body: reqInfo.body
                }
            }
            if (response) {
                let len = response.body ? response.body.length : 0;
                await this.db.useCache.insert({ uid: this.userInfo._id, url: reqInfo.url, method: reqInfo.method, response, length: len });
                await this.db.useLog.update({ uid: this.userInfo._id, url: reqInfo.url }, { $set: { cache: true } }, { multi: true });
            }
            reqInfo.cache = true;
            return reqInfo;
        } catch (e) {
            throw e;
        }
    }
    /**
     * 用户删除缓存
     * @param {any} id
     */
    async removeCache(id) {
        try {
            this.userInfo = await this.db.user.findOne({ openId: this.openId });
            await this.db.useCache.remove({  _id: fun.ObjectId(id) , uid: this.userInfo._id });
            return null;
        } catch (e) {
            throw e;
        }
    }
    /**
     * 获取用户缓存列表
     * @param page
     * @param day
     * @param params
     * @returns {Promise<{totalItem: *, list: *}>}
     */
    async getCacheList(page = 1, day = null, params = null) {
        try {
            if (!day) day = new Date();
            let maxDay = new Date(day.setHours(24, 0, 0, 0)), minDay = new Date(day.setHours(0, 0, 0, 0)), perPage = 10;
            this.userInfo = await this.db.user.findOne({ openId: this.openId });
            let query = { uid: this.userInfo._id };
            if(params) query.url = {$regex:params.replace(/([\^\$\(\)\*\+\?\.\\\|\[\]\{\}])/g, "\\$1")};
            let totalItem = await this.db.useCache.count(query);
            let totalPage = Math.ceil(totalItem / perPage);
            page = page <= totalPage ? page : totalPage;
            page = page > 0 ? page : 1;
            let list = await this.db.useCache.find(query, { limit: perPage, skip: (page - 1) * perPage, project: {response:0}});
            return { totalItem, list }
        } catch (e) {
            throw e;
        }
    }
    /**
     * 用户的正则过滤列表
     * @returns {Promise<void>}
     */
    async regularList(page = 1, day = null){
        try{
            if (!day) day = new Date();
            let maxDay = new Date(day.setHours(24, 0, 0, 0)), minDay = new Date(day.setHours(0, 0, 0, 0)), perPage = 10;
            this.userInfo = await this.db.user.findOne({ openId: this.openId });
            let query = { uid: this.userInfo._id };
            let totalItem = await this.db.useRule.count(query);
            let totalPage = Math.ceil(totalItem / perPage);
            page = page <= totalPage ? page : totalPage;
            page = page > 0 ? page : 1;
            let list = await this.db.useRule.find(query, { limit: perPage, skip: (page - 1) * perPage, sort: { created_at: -1 } });
            return { totalItem, list }
        }catch (e) {
            throw e;
        }
    }
    /**
     * 添加新的正则过滤
     * @param rule
     * @param _id
     * @returns {Promise<void>}
     */
    async updateRegular(rule, _id = null){
        try{
            let ruleReg = new RegExp(rule);
            this.userInfo = await this.db.user.findOne({ openId: this.openId });
            let ruleInfo = await this.db.useRule.findOne({ uid: this.userInfo._id, rule });
            if(!ruleInfo){
                if(_id){
                    await this.db.useRule.update({_id:fun.ObjectId(_id)},{ $set:{rule, updated_at: new Date()}});
                    return await this.db.useRule.findOne({ _id:fun.ObjectId(_id) });
                } else {
                    let insertInfo = await this.db.useRule.insert({ uid: this.userInfo._id, rule, created_at: new Date() });
                    return insertInfo.ops[0];
                }
            } else {
                throw new Error("10014");
            }
        }catch (e) {
            if(e.message.indexOf("Invalid regular expression")>=0){
                throw new Error("10015");
            } else {
                throw e;
            }
        }
    }
    /**
     * 删除正则过滤
     * @returns {Promise<void>}
     */
    async removeRegular(_id = null){
        try{
            this.userInfo = await this.db.user.findOne({ openId: this.openId });
            await this.db.useRule.remove({ _id: fun.ObjectId(_id) , uid: this.userInfo._id});
            return null;
        }catch (e) {
            throw e;
        }
    }
    /**
     * 展示用户使用情况
     * @returns {Promise<void>}
     */
    async useLog(day = 7){
        try{
            let now = new Date(new Date().setHours(24, 0, 0, 0) - day * 24 * 60 * 60 * 1000), dayObj = {}, dataArr = [], xArr = [];
            this.userInfo = await this.db.user.findOne({ openId: this.openId });
            let useLogList = await this.db.packageUse.find({ uid: this.userInfo._id, day:{$gte:now}});
            for(let log of useLogList){
                let day = DATE.formatDate(log.day);
                dayObj[day] = dayObj[day] ? dayObj[day] + log.count : log.count;
            }
            console.log(dayObj);
            let max = 0;
            for(let i = 0; i<day; i++){
                let thisDay = new Date(new Date().setHours(24, 0, 0, 0) - (day - i) * 24 * 60 * 60 * 1000);
                let thisDayStr = DATE.formatDate(thisDay);
                dayObj[thisDayStr] = dayObj[thisDayStr] ? dayObj[thisDayStr] : 0;
                max = max > dayObj[thisDayStr] ? max : dayObj[thisDayStr] ;
                dataArr.push([i, dayObj[thisDayStr] ? dayObj[thisDayStr]  : 0]);
                xArr.push([i, thisDayStr])
            }
            max = Math.ceil(max / 1000) * 1000;
            return {dataArr, max, xArr}
        }catch (e) {
            throw e;
        }
    }
}
module.exports = USER;